
#if defined(WIN32) || defined(WIN64)
#pragma unmanaged
#endif

#include <Windows.h>

#include <memory>
#include <Liberty3D/Engine/Context.h>
#include <Liberty3D/Engine/Component.h>
#include <Liberty3D/Engine/Conversion.h>
#include <Liberty3D/Engine/Game.h>
#include <Liberty3D/Engine/GameObject.h>
#include <Liberty3D/Engine/Performance.h>
#include <Liberty3D/Engine/Window.h>

using namespace Liberty::Engine;

int CALLBACK wWinMain(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR lpCommandLine, int nShowCmd) {
    try {
        Performance::init();
        Window::init(hInst);
        Game::init();

        MSG                        msg;
        std::shared_ptr<Context>   context(new Context());
        Context*                   contextP(context.get());
        Performance::Counter       nextTick = 0;
        const Performance::Counter oneMillisecond(Performance::oneMillisecond);

        Game::starting(context);
        contextP->start();
        Game::started(context);
        Performance::query(nextTick);

        while (true) {
            Performance::Counter current;
            DWORD                delay;

            Performance::query(current);
            
            if (current >= nextTick) {
                delay = 0;
            }
            else {
                delay = (DWORD)((nextTick - current) / oneMillisecond);
            }

            DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, delay, QS_ALLINPUT);

            if (result == WAIT_OBJECT_0) {
                if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) {
                    TranslateMessage(&msg);

                    try {
                        DispatchMessage(&msg);
                    }
                    catch (const std::exception& ex) {
                        context->handleException(ex);
                    }
                }
            }
            else if (result == WAIT_TIMEOUT) {
                try {
                    contextP->tick(nextTick);
                }
                catch (const std::exception& ex) {
                    context->handleException(ex);
                }
            }
            else if (result == WAIT_FAILED) {
                throw std::runtime_error("MsgWaitForMultipleObjects failed.");
            }
        }

        Game::stoping(context);
        contextP->stop();
        Game::stoped(context);
    }
    catch (const std::exception& ex) {
        MessageBox(NULL, UTF8_To_UTF16(ex.what()).c_str(), NULL, MB_ICONERROR);
    }

    return 0;
}
